package mt.dummy;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;


import java.net.Socket;
import java.util.Collection;
import java.util.Iterator;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;



import mt.Order;
import mt.comm.ClientSideMessage;
import mt.comm.ServerComm;
import mt.comm.ServerSideMessage;
import mt.comm.ServerSideMessage.Type;
/**
 * ServerCommDummy is used by the server to communicate with the clients.
 * @author Pedro,Rita, Miguel
 *
 */
public class ServerCommDummy implements ServerComm {
	/**
	 * The Port of the ServerCommDummy
	 */
	public static final int PORTO = 1901;
	
	/**
	 * The ServerSocket of the ServerCommDummy
	 */
	private ServerSocket serverSocket;

	/**
	 * The queue that has ServerMessage
	 */
	public BlockingQueue<ServerMessage> q=new ArrayBlockingQueue<ServerMessage>(100);
	
	/**
	 * The hashmap that has the nickname of the client and the output to answer him.
	 */

	public HashMaps <String, ObjectOutputStream> maps= new HashMaps<String,ObjectOutputStream>();
	

	@Override
	public void start() {

		new StartServicing().start();

	}
	/**
	 *  StartServicing setups a server socket and stars another thread that listens for connections. 
	 */
	class StartServicing extends Thread {
		
		
		
		@Override
		public void run() {
			try {
				serverSocket = new ServerSocket(PORTO);

				System.out.println("ServerSocket activa");
				while (true) { // espera q venha clients
					Socket socket = serverSocket.accept(); //
					ObjectOutputStream out = new ObjectOutputStream(
							socket.getOutputStream());

					ObjectInputStream in = new ObjectInputStream(
							socket.getInputStream());
			
					
					
					new DealWithClient(in, out).start();
					System.out.println("novo cliente");
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			
		}
		
		
		
	}
	/**
	 *  This thread listens for connections. This Threads receives and processed the ServerMessage.
	 */


	class DealWithClient extends Thread {

		private ObjectInputStream in;
		private ObjectOutputStream out;
		private String clientName;

		public DealWithClient(ObjectInputStream in, ObjectOutputStream out) {
			// TODO Auto-generated constructor stub
			this.in = in;
			this.out = out;
		}
		
		public ObjectOutputStream getOut(){
			return out;
		}		
		
		@Override
		public void run() {
			try {
				while (true) {
					ServerMessage message = (ServerMessage) in.readObject();
					
					switch (message.getType()) {
					case CONNECTED:
							if(!maps.contains(message.getSenderNickname())){
								clientName=message.getSenderNickname();

								maps.addEntry(message.getSenderNickname(), out);
								q.add(message);
							}
							else
							{
								ClientMessage msg= new ClientMessage(ClientSideMessage.Type.ERROR,  "ja existe esse nome");
								out.writeObject(msg);
							}
					break;
					
					case NEW_ORDER:
						q.add(message);
					   break;
					default:
						break;
					}
	

				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				ServerMessage msg= new ServerMessage(ServerSideMessage.Type.DISCONNECTED, clientName,null);
				q.clear();
				q.add(msg);
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}

	}



	@Override
	public boolean hasNextMessage() {
		return !q.isEmpty();
	}

	@Override
	public ServerSideMessage getNextMessage() {
		return q.poll();
	}

	@Override
	public void sendError(String toNickname, String error) {
		ObjectOutputStream out= maps.get(toNickname);
		try {
			out.writeObject(new ClientMessage(ClientSideMessage.Type.ERROR, "erro"));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	@Override
	public boolean clientIsConnected(String nickname) {		
		return maps.contains(nickname);
	}

	@Override
	public void disconnectClient(String nickname) {

		maps.remove(nickname);
	}

	@Override
	public void sendOrder(String receiversNickname, Order order) {
		if(clientIsConnected(receiversNickname)){
			ObjectOutputStream out= maps.get(receiversNickname);
			try {
				out.writeObject(new ClientMessage(ClientSideMessage.Type.ORDER,order));
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		

	}
	
}